import type { ReactElement } from 'react';
import { Checkbox, Form, PageSection, Text, TextInput } from '@patternfly/react-core';
import * as yup from 'yup';
import merge from 'lodash/merge';

import type { ImageIntegrationBase } from 'services/ImageIntegrationsService';

import useMetadata from 'hooks/useMetadata';
import ExternalLink from 'Components/PatternFly/IconText/ExternalLink';
import FormMessage from 'Components/PatternFly/FormMessage';
import FormTestButton from 'Components/PatternFly/FormTestButton';
import FormSaveButton from 'Components/PatternFly/FormSaveButton';
import FormCancelButton from 'Components/PatternFly/FormCancelButton';
import { getVersionedDocs } from 'utils/versioning';

import usePageState from '../../hooks/usePageState';
import useIntegrationForm from '../useIntegrationForm';
import type { IntegrationFormProps } from '../integrationFormTypes';

import IntegrationFormActions from '../IntegrationFormActions';
import FormLabelGroup from '../FormLabelGroup';

import IntegrationHelpIcon from './Components/IntegrationHelpIcon';

export type AzureIntegration = {
    categories: 'REGISTRY'[];
    azure: {
        endpoint: string;
        username: string;
        password: string;
        wifEnabled: boolean;
    };
    type: 'azure';
} & ImageIntegrationBase;

export type AzureIntegrationFormValues = {
    config: AzureIntegration;
    updatePassword: boolean;
};

export type DockerIntegration = {
    categories: 'REGISTRY'[];
    docker: {
        endpoint: string;
        username: string;
        password: string;
        insecure: boolean;
    };
    type: 'azure';
} & ImageIntegrationBase;

export const validationSchema = yup.object().shape({
    config: yup.object().shape({
        name: yup.string().trim().required('An integration name is required'),
        categories: yup
            .array()
            .of(yup.string().trim().oneOf(['REGISTRY']))
            .min(1, 'Must have at least one type selected')
            .required('A category is required'),
        azure: yup.object().shape({
            endpoint: yup.string().trim().required('An endpoint is required'),
            username: yup.string(),
            password: yup
                .string()
                .test(
                    'password-test',
                    'A password is required',
                    (value, context: yup.TestContext) => {
                        const requirePasswordField =
                            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                            // @ts-ignore
                            context?.from[2]?.value?.updatePassword || false;
                        const useWorkloadId = context?.parent?.wifEnabled;

                        if (!requirePasswordField || useWorkloadId) {
                            return true;
                        }

                        const trimmedValue = value?.trim();
                        return !!trimmedValue;
                    }
                ),
            wifEnabled: yup.boolean(),
        }),
        skipTestIntegration: yup.boolean(),
        type: yup.string().matches(/azure/),
    }),
    updatePassword: yup.boolean(),
});

export const defaultValues: AzureIntegrationFormValues = {
    config: {
        id: '',
        name: '',
        categories: ['REGISTRY'],
        azure: {
            endpoint: '',
            username: '',
            password: '',
            wifEnabled: false,
        },
        autogenerated: false,
        clusterId: '',
        clusters: [],
        skipTestIntegration: false,
        type: 'azure',
    },
    updatePassword: true,
};

function AzureIntegrationForm({
    initialValues = null,
    isEditable = false,
}: IntegrationFormProps<AzureIntegration | DockerIntegration>): ReactElement {
    const formInitialValues = structuredClone(defaultValues);
    if (initialValues) {
        // Note that integrations of type "azure" support both `DockerConfig` (deprecated in 4.7) and `AzureConfig`.
        // If the `DockerConfig` schema is used, we convert to `AzureConfig`.
        // TODO(ROX-27720): remove support for `DockerConfig`.
        if ('docker' in initialValues) {
            formInitialValues.config = {
                id: initialValues.id,
                name: initialValues.name,
                categories: initialValues.categories,
                azure: {
                    endpoint: initialValues.docker.endpoint,
                    username: initialValues.docker.username,
                    password: initialValues.docker.password,
                    wifEnabled: false,
                },
                autogenerated: initialValues.autogenerated,
                clusterId: initialValues.clusterId,
                clusters: initialValues.clusters,
                skipTestIntegration: initialValues.skipTestIntegration,
                type: initialValues.type,
            };
        } else {
            merge(formInitialValues.config, initialValues);
        }

        // We want to clear the password because backend returns '******' to represent that there
        // are currently stored credentials.
        formInitialValues.config.azure.password = '';

        // Don't assume user wants to change password; that has caused confusing UX.
        // If workload identity is enabled, we always overwrite any existing credentials.
        formInitialValues.updatePassword = formInitialValues.config.azure.wifEnabled;
    }
    const {
        values,
        touched,
        errors,
        dirty,
        isValid,
        setFieldValue,
        handleBlur,
        isSubmitting,
        isTesting,
        onSave,
        onTest,
        onCancel,
        message,
    } = useIntegrationForm<AzureIntegrationFormValues>({
        initialValues: formInitialValues,
        validationSchema,
    });
    const { version } = useMetadata();
    const { isCreating } = usePageState();

    function onChange(value, event) {
        return setFieldValue(event.target.id, value);
    }

    function onUpdateCredentialsChange(value, event) {
        setFieldValue('config.azure.password', '');
        return setFieldValue(event.target.id, value);
    }

    function onUpdateWorkloadIdentityChange(value, event) {
        if (!isCreating) {
            setFieldValue('updatePassword', value);
        }
        setFieldValue('config.azure.username', '');
        setFieldValue('config.azure.password', '');
        return setFieldValue(event.target.id, value);
    }

    return (
        <>
            <PageSection variant="light" isFilled hasOverflowScroll>
                <FormMessage message={message} />
                <Form isWidthLimited>
                    <FormLabelGroup
                        label="Integration name"
                        isRequired
                        fieldId="config.name"
                        touched={touched}
                        errors={errors}
                    >
                        <TextInput
                            isRequired
                            type="text"
                            id="config.name"
                            placeholder="(ex. Azure Registry)"
                            value={values.config.name}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                    <FormLabelGroup
                        label="Endpoint"
                        isRequired
                        labelIcon={
                            <IntegrationHelpIcon
                                helpTitle="Endpoint"
                                helpText={
                                    <div>
                                        The endpoint under which the Azure container registry is
                                        reached.
                                    </div>
                                }
                                ariaLabel="Help for endpoint"
                            />
                        }
                        fieldId="config.azure.endpoint"
                        touched={touched}
                        errors={errors}
                    >
                        <TextInput
                            isRequired
                            type="text"
                            id="config.azure.endpoint"
                            placeholder="(ex. <registry>.azurecr.io)"
                            value={values.config.azure.endpoint}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                    <FormLabelGroup
                        label="Short-lived tokens"
                        labelIcon={
                            <IntegrationHelpIcon
                                helpTitle="Use workload identity"
                                helpText={
                                    <>
                                        <Text>
                                            Enables authentication with short-lived tokens using
                                            Azure managed identities or Azure workload identities.
                                            The associated identity requires permission to pull
                                            images from the registry.
                                        </Text>
                                        <Text>
                                            For more information, see{' '}
                                            <ExternalLink>
                                                <a
                                                    href={getVersionedDocs(
                                                        version,
                                                        'integrating/integrate-using-short-lived-tokens'
                                                    )}
                                                    target="_blank"
                                                    rel="noopener noreferrer"
                                                >
                                                    RHACS documentation
                                                </a>
                                            </ExternalLink>
                                        </Text>
                                    </>
                                }
                                ariaLabel="Help for short-lived tokens"
                            />
                        }
                        fieldId="config.azure.wifEnabled"
                        helperText="Enabling short-lived tokens removes any existing credentials from this integration"
                        touched={touched}
                        errors={errors}
                    >
                        <Checkbox
                            label="Use workload identity"
                            id="config.azure.wifEnabled"
                            isChecked={values.config.azure.wifEnabled}
                            onChange={(event, value) =>
                                onUpdateWorkloadIdentityChange(value, event)
                            }
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                    {!isCreating && isEditable && (
                        <FormLabelGroup
                            fieldId="updatePassword"
                            helperText="Enable this option to replace currently stored credentials (if any)"
                            errors={errors}
                        >
                            <Checkbox
                                label="Update stored credentials"
                                id="updatePassword"
                                isChecked={!values.config.azure.wifEnabled && values.updatePassword}
                                onChange={(event, value) => onUpdateCredentialsChange(value, event)}
                                onBlur={handleBlur}
                                isDisabled={!isEditable || values.config.azure.wifEnabled}
                            />
                        </FormLabelGroup>
                    )}
                    <FormLabelGroup
                        label="Username"
                        labelIcon={
                            <IntegrationHelpIcon
                                helpTitle="Username"
                                helpText={
                                    <div>
                                        The username to authenticate with the Azure container. The
                                        associated identity requires permission to pull images from
                                        the registry. For access keys use the attached username. For
                                        generated tokens use the name of the token.
                                    </div>
                                }
                                ariaLabel="Help for username"
                            />
                        }
                        fieldId="config.azure.username"
                        touched={touched}
                        errors={errors}
                    >
                        <TextInput
                            type="text"
                            id="config.azure.username"
                            value={values.config.azure.username}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={
                                !isEditable ||
                                !values.updatePassword ||
                                values.config.azure.wifEnabled
                            }
                        />
                    </FormLabelGroup>
                    <FormLabelGroup
                        label="Password"
                        labelIcon={
                            <IntegrationHelpIcon
                                helpTitle="Password"
                                helpText={
                                    <div>
                                        The password to authenticate with the Azure container. The
                                        associated identity requires permission to pull images from
                                        the registry. For access keys and tokens use the attached
                                        password.
                                    </div>
                                }
                                ariaLabel="Help for password"
                            />
                        }
                        isRequired={!values.config.azure.wifEnabled && values.updatePassword}
                        fieldId="config.azure.password"
                        touched={touched}
                        errors={errors}
                    >
                        <TextInput
                            isRequired={!values.config.azure.wifEnabled && values.updatePassword}
                            type="password"
                            id="config.azure.password"
                            value={values.config.azure.password}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={
                                !isEditable ||
                                !values.updatePassword ||
                                values.config.azure.wifEnabled
                            }
                            placeholder={
                                values.config.azure.wifEnabled || values.updatePassword
                                    ? ''
                                    : 'Currently-stored password will be used.'
                            }
                        />
                    </FormLabelGroup>
                    <FormLabelGroup
                        fieldId="config.skipTestIntegration"
                        touched={touched}
                        errors={errors}
                    >
                        <Checkbox
                            label="Create integration without testing"
                            id="config.skipTestIntegration"
                            aria-label="skip test integration"
                            isChecked={values.config.skipTestIntegration}
                            onChange={(event, value) => onChange(value, event)}
                            onBlur={handleBlur}
                            isDisabled={!isEditable}
                        />
                    </FormLabelGroup>
                </Form>
            </PageSection>
            {isEditable && (
                <IntegrationFormActions>
                    <FormSaveButton
                        onSave={onSave}
                        isSubmitting={isSubmitting}
                        isTesting={isTesting}
                        isDisabled={!dirty || !isValid}
                    >
                        Save
                    </FormSaveButton>
                    <FormTestButton
                        onTest={onTest}
                        isSubmitting={isSubmitting}
                        isTesting={isTesting}
                        isDisabled={!isValid}
                    >
                        Test
                    </FormTestButton>
                    <FormCancelButton onCancel={onCancel}>Cancel</FormCancelButton>
                </IntegrationFormActions>
            )}
        </>
    );
}

export default AzureIntegrationForm;
